#include <iostream>
#include <cassert>
#include <sys/epoll.h>

#include "Sock.hpp"
#include "log.hpp"
#include "err.hpp"
#include "Epoller.hpp"

const static uint16_t defaultport = 8888;

class EpollServer
{
    const static int gnum = 64;

public:
    EpollServer(uint16_t port = defaultport) : _port(port)
    {
    }
    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        _epoller.Create();
    }

    void HandlerEvent(int num)
    {
        for (int i = 0; i < num; ++i)
        {
            uint32_t revent = _revs[i].events;
            int fd = _revs[i].data.fd;
            if (revent & EPOLLIN)
            {
                if (fd == _listensock.Fd())
                {
                    std::string clientIp;
                    uint16_t clientPort;
                    // 连接事件
                    int sock = _listensock.Accept(&clientIp, &clientPort);
                    if (sock < 0)
                        continue;
                    std::string name = "[" + clientIp + std::to_string(clientPort) + "]";
                    logMessage(Info, "连接 %s 到来,sock:%d", name.c_str(), sock);
                    bool r = _epoller.AddEvent(sock, EPOLLIN);
                    assert(r);
                    (void)r;
                }
                else
                {
                    // 读事件到来
                    char buffer[1024];
                    int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
                    if (n < 0)
                        logMessage(Warning, "recv error,%d:%s", errno, strerror(errno));
                    else if (n > 0)
                    {
                        buffer[n] = '\0';
                        std::cout << "client# " << buffer << std::endl;
                        // 返回响应
                        std::string echo = "[server echo] ";
                        echo += buffer;
                        send(fd, echo.c_str(), echo.size(), 0);
                    }
                    else
                    {
                        // 客户端关闭连接
                        logMessage(Info, "client quit...%d", fd);
                        //要先移除事件，epoll_ctl不允许移除不存在或关闭的文件描述符
                        _epoller.DelEvent(fd);
                        close(fd);
                    }
                }
            }
        }
    }

    void Start()
    {
        // 将监听套接字链入epoll模型，关心读事件
        bool r = _epoller.AddEvent(_listensock.Fd(), EPOLLIN);
        assert(r);
        (void)r;

        int timeout = -1;
        while (true)
        {
            int n = _epoller.Wait(_revs, gnum, timeout);
            switch (n)
            {
            case 0:
                logMessage(Info, "timeout...%d:%s", errno, strerror(errno));
                break;
            case -1:
                logMessage(Warning, "epoll wait errno,%d:%s", errno, strerror(errno));
                break;
            default:
                HandlerEvent(n);
                break;
            }
        }
    }

    ~EpollServer()
    {
        _listensock.Close();
        _epoller.Close();
    }

private:
    uint16_t _port;                 // 端口号
    Sock _listensock;               // 监听套接字
    Epoller _epoller;               // epoll模型
    struct epoll_event _revs[gnum]; // 就绪事件集合
};